#include "zigbeeDevice.h"
#include "readConig.h"

#include <unistd.h>
#include <cstring>
#include <stdint.h>

const int MAXLEN = 1024;

void zigbeeDevice::set_speed(int speed)
{
	int status;
	struct termios Opt;

	tcgetattr(serial_fd, &Opt); 	//get attribute of serial port
	tcflush(serial_fd, TCIOFLUSH);
	cfsetispeed(&Opt, speed);
	cfsetospeed(&Opt, speed);

	status = tcsetattr(serial_fd, TCSANOW, &Opt); 	//set attribute
	if(status != 0)
	{
		perror("tcsetattr fd");
		return;
	}

	tcflush(serial_fd, TCIOFLUSH);
}

int zigbeeDevice::set_data_format(int databits, int stopbits, int parity)
{
	struct termios opt;

	if(tcgetattr(serial_fd, &opt) != 0)
	{
		perror("set up serial 1");
		return -1;
	}

	opt.c_cflag &= ~CSIZE;

	switch(databits)
	{
		case 5:
			opt.c_cflag |= CS5;
			break;
		case 6:
			opt.c_cflag |= CS6;
			break;
		case 7:
			opt.c_cflag |= CS7;
			break;
		case 8:
			opt.c_cflag |= CS8;
			break;
		default:
			fprintf(stderr, "Unsupported data size\n");
			return -1;
	}

	switch(parity)
	{
		case 'n':
		case 'N':
			opt.c_cflag &= ~PARENB; /* Clear parity enable */
			opt.c_iflag &= ~INPCK; 	/* Enable parity checking */
			break;
		case 'o':
		case 'O':
			opt.c_cflag |= (PARODD | PARENB); /* parity checking */
			opt.c_iflag |= INPCK; 					/* Disnable parity checking */
			break;
		case 'e':
		case 'E':
			opt.c_cflag |= PARENB; 	/* Enable parity */
			opt.c_cflag &= ~PARODD; /* */
			opt.c_iflag |= INPCK; 	/* Disnable parity checking */
			break;
		default:
			fprintf(stderr, "Unsppouted parity\n");
			return -1;
	}

	switch(stopbits)
	{
		case 1:
			opt.c_cflag &= ~CSTOPB;
			break;
		case 2:
			opt.c_cflag |= CSTOPB;
			break;
		default:
			fprintf(stderr, "Unspported stop bits\n");
			return -1;
	}

	/* Set iput parity option */
	if(parity != 'n')
		opt.c_iflag |= INPCK;
	opt.c_cc[VTIME] = 100;
	opt.c_cc[VMIN] = 0;

	tcflush(serial_fd, TCIFLUSH); 	/* Update the options and do it NOW */
	if(tcsetattr(serial_fd, TCSANOW, &opt) != 0)
	{
		perror("Set up serial 3");
		return -1;
	}

	return 0;
}

unsigned short int zigbeeDevice::GetCrc16(char* InStr, unsigned int len)
{
	unsigned short int crc = 0;
	//cout<<"sizeof crc "<<sizeof(crc)<<endl;
	for(int i = 0; i < len; i++)
	{
		crc = crc ^ InStr[i] << 8;
		for(int j = 0; j < 8; j++)
		{
			if((crc & 0x8000) != 0)
				crc = (((crc << 1) ^ 0x1021)) & 0xffff;
			else 
				crc = (crc<<1) & 0xffff;
		}		
		//cout<<crc<<endl;
	}

	//cout<<crc<<endl;
	return crc & 0xffff;

} 

zigbeeDevice::zigbeeDevice(int *ppMap)
{
	this->ppMap = ppMap;

	//读配置文件
	Config configSettings("conf");
	string serialPort;
	int serialSpeed;
	serialPort = configSettings.Read("serial_port", serialPort);
	serialSpeed = configSettings.Read("serial_speed", 0);

	//串口读写fd
	//serial_fd = open_serial_port(serialPort);
	//setSerial(serialSpeed);
	
	serial_fd = open(serialPort.c_str(), O_RDWR);
	if(serial_fd == -1)
	{
		printf("Can't open %s !\n", serialPort.c_str());
		exit(0);
	}

	set_speed(serialSpeed);

	if(set_data_format(8, 1, 'N') == -1)
	{
		printf("date format error");
		exit(1);
	}
	
	//填写本地地址信息
	bzero(&commitAddr, sizeof(commitAddr));
	commitAddr.sin_family = AF_INET;
	commitAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	//获得socket
	commit_sockfd = Socket(AF_INET, SOCK_STREAM, 0);
}

zigbeeDevice::~zigbeeDevice()
{
	Close(serial_fd);
	Close(commit_sockfd);
	delete readThread;
}

void zigbeeDevice::setSerial(const int speed)
{
	struct termios options;

	/*
	 *Get the current options for the port...
	 */
	tcgetattr(serial_fd, &options);

	/*
	 *Set the baud rates to speed...
	 */
	 
	int speed_arr[] = {B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300, };
	int name_arr[] = {115200, 38400, 19200, 9600, 4800, 2400, 1200, 300, };
	
	int i = 0;
	for(; i < sizeof(name_arr) / sizeof(int); i++)
	{
		if(speed == name_arr[i])
			break;
	}
	
	if(i >= sizeof(name_arr) / sizeof(int))
	{
		cerr<<"no such speed"<<endl;;
		exit(1);
	}
	
	cfsetispeed(&options, speed_arr[i]);
	cfsetospeed(&options, speed_arr[i]);
	
	/*
	 *Set Parity check
	 */
	options.c_cflag &= ~PARENB;
	options.c_cflag &= ~CSTOPB;
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS8;
	/*
	 *Set hardware flow control
	 */
	//options.c_cflag &= ~CNEW_RTSCTS;

	/*
	 *Enable the receiver and set local mode ...
	 */
	options.c_cflag |= (CLOCAL | CREAD);
	/*
	 *Set the new options for the port...
	 */
	tcsetattr(serial_fd, TCSANOW, &options);

	fcntl(serial_fd, F_SETFL, 0);
}

int zigbeeDevice::open_serial_port(const string &serial_port)
{
	int fd;

	//fd = Open(serial_port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
	fd = Open(serial_port.c_str(), O_RDWR);
	cout<<"open serial port "<<serial_port.c_str()<<endl;
	Fcntl(fd, F_SETFL, 0);

	return fd;
}

void zigbeeDevice::startDevice()
{
	readThread = new Thread(this);
	start();				//启动主线程
	readThread->start();	//启动设备读线程
}

void zigbeeDevice::run()
{
	if(Thread::isEquals(readThread))
	{
		cout<<"startZigbeeDevice"<<endl;

		int buff[MAXLEN];
		int n;
		
		//获得port
		int commit_port = 9000;
		
		while(true)
		{	
			cout<<"serial read"<<endl;
			n = Readn(serial_fd, buff, MAXLEN);
			cout<<"n: "<<n<<endl;
			sleep(1);
			cout<<"serial done"<<endl;
			//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
			//从串口读来的数据中获得程序号，根据程序号读取port号
			//获得port
			int commit_port = 9000;

			//填写对应进程的端口
			commitAddr.sin_port = htons(commit_port);
			//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
			
			//向上层提交数据
			//Sendto(commit_sockfd, buff, n, 0, (SA *)&commitAddr, sizeof(SA));
			//cout<<"serial send d"
		}
	}
}

void zigbeeDevice::sendMessage(int nodeID, char *buff, int cbuffSize)
{
	cout<<"serial_fd: "<<serial_fd<<endl;
	
	int buffSize = 22;
	
	/*
	int8_t cbuff[22];
	bzero(cbuff, sizeof(cbuff));
	cbuff[0] = 0;
	cbuff[1] = 2;
	cbuff[2] = 0;
	cbuff[3] = 3;
	//strncpy((char *)(cbuff + 2), "hello", sizeof("hello"));
	*/
	buffSize = 9;
	int8_t cbuff[9];
	memcpy(cbuff, "\x70\x01\x01\x00\x17\x32\x33\x34\x35", 9);

	cout<<"buff: |"<<cbuff<<"|buffSize: "<<buffSize<<endl;
	char resault[200];
	bzero(resault, sizeof(resault));

	int n = 0;
	
	//header
	memcpy(resault + n, "\x44\x00\xff\xff\x00\x00", 7);
	n += 7;

	//length
	//memcpy(resault + n, (void *)&buffSize, 1);
	resault[n] = char(buffSize);
	n += 1;

	memcpy(resault + n, "\x00\x00\x89", 2);
	n += 2;

	//data
	memcpy(resault + n, cbuff, buffSize);
	n += buffSize;
	
	//crc
	unsigned int crc = GetCrc16(resault, n);
	cout<<crc<<endl;
	memcpy(resault + n, (void *)&crc, 2);
	n += 2;

	//cout<<"n"<<n<<endl;
	char ret[200];
	bzero(ret, sizeof(ret));
	int i, j = 1;

	ret[0] = '\x7e'; 	//header

	//change '\x7d' to '\x7d\x5e' and '\x7e' to '\x7d\x5e'
	for(i = 0; i < n; i++)
	{
		//printf("%02x ", (unsigned char)resault[i]);
		if(resault[i] == '\x7d')
		{
			ret[i + j] = '\x7d';
			ret[i + j + 1] = '\x5d';
			j++;
		}
		else if(resault[i] == '\x7e')
		{
			ret[i + j] = '\x7d';
			ret[i + j + 1] = '\x5e';
			j++;
		}
		else
			ret[i + j] = resault[i];
		//cout<<"i: "<<i<<" j: "<<j<<endl;
	}
	ret[n + j] = '\x7e'; 	//tail

	cout<<endl;

	for(i = 0; i <= n + j; i++)
	{
		printf("\\x%02x", (unsigned char)ret[i]);
	}
	cout<<endl;

	Writen(serial_fd, ret, n + j + 1);
}
